/*
   +----------------------------------------------------------------------+   
   |                                                                      |
   |                     OCILIB - C Driver for Oracle                     |
   |                                                                      |
   |                      (C Wrapper for Oracle OCI)                      |
   |                                                                      |
   +----------------------------------------------------------------------+
   |                      Website : http://ocilib.net                     |
   +----------------------------------------------------------------------+
   |               Copyright (c) 2007-2008 Vincent ROGIER                 |
   +----------------------------------------------------------------------+
   | This library is free software; you can redistribute it and/or        |
   | modify it under the terms of the GNU Library General Public          |
   | License as published by the Free Software Foundation; either         |
   | version 2 of the License, or (at your option) any later version.     |
   |                                                                      |
   | This library is distributed in the hope that it will be useful,      |
   | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
   | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
   | Library General Public License for more details.                     |
   |                                                                      |
   | You should have received a copy of the GNU Library General Public    |
   | License along with this library; if not, write to the Free           |
   | Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   |
   +----------------------------------------------------------------------+
   |          Author: Vincent ROGIER <vince.rogier@gmail.com>             |
   +----------------------------------------------------------------------+ 
*/

/* ------------------------------------------------------------------------ *
 * $Id: directpath.c, v 3.1.0 2008/10/23 21:00 Vince $
 * ------------------------------------------------------------------------ */

#include "ocilib_internal.h"


typedef struct OCI_DirectPath 
{
    OCI_Connection *con;
    OCI_Schema *sch;

    OCIDirPathCtx       *ctx;
    OCIDirPathColArray  *arr;
    OCIDirPathStream    *strm;

    ub4 size;

    mtext *table;

} OCI_DirectPath;   



/* ************************************************************************ *
 *                            PRIVATE FUNCTIONS
 * ************************************************************************ */

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathUpdate
 * ------------------------------------------------------------------------ */

boolean OCI_DirectPathUpdate(OCI_DirectPath *dp)
{
    boolean res = TRUE;

    OCI_CHECK(dp == NULL, FALSE);

    /* arrray -> stream */
	
    OCI_CALL2
    (
        res, dp->con, 
        
        OCIDirPathColArrayToStream(dp->arr, dp->ctx,  dp->strm,  dp->con->err,
                                   dp->size, (ub4) 0)
    )

    /* load stream */

    OCI_CALL2
    (
        res, dp->con, 
        
        OCIDirPathLoadStream(dp->ctx, dp->strm, dp->con->err)
    )                          
                                                 
    OCI_RESULT(res);

    return res;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathReset
 * ------------------------------------------------------------------------ */

boolean  OCI_DirectPathReset(OCI_DirectPath *dp)
{
    boolean res = TRUE;

    OCI_CHECK(dp == NULL, FALSE);
                                            
    /* reset array */
	
    OCI_CALL2
    (
        res, dp->con, 
        
        OCIDirPathColArrayReset(dp->arr, dp->con->err)
    )

    /* reset array */

		
    OCI_CALL2
    (
        res, dp->con, 
        
        OCIDirPathStreamReset(dp->strm, dp->con->err)
    )

    OCI_RESULT(res);

    return res;
}

/* ************************************************************************ *
 *                            PUBLIC FUNCTIONS
 * ************************************************************************ */

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathCreate
 * ------------------------------------------------------------------------ */

OCI_DirectPath * OCI_API OCI_DirectPathCreate(OCI_Connection *con, mtext *table,
                                              int size)
{
    OCI_DirectPath *dp;
    OCIParam *hattr=NULL, *hlist=NULL;
    OCI_Schema *schema;
    OCI_Column * cols;
    int i;
    ub1 toto = 1;

    OCI_CHECK_PTR(OCI_IPC_COLLECTION, con, NULL);
    OCI_CHECK_PTR(OCI_IPC_STRING, table, NULL);

    /* get table schema infos */
    
    schema = OCI_SchemaGet(con, name, OCI_SCHEMA_TABLE);

    /* if table description failed, returns NULL */
    OCI_CHECK_PTR(table, NULL);

    /* allocates object*/
    OCI_ALLOC(dp, mem_size, sizeof(OCI_DirectPath));

    if (dp == NULL)
    {
        OCI_ErrorMemory(con, NULL, mem_size);
        OCI_SchemaFreeTable(table);
        return NULL;
    }

   /* set attributes */
    dp->con   = con;
    dp->table = table;
    dp->size  = (ub2) size;

    /* allocates buffer array*/
    OCI_ALLOC(dp->items, mem_size, sizeof(OCI_DirectPathItem) * table->nb_items);

    if (dp->items == NULL)
    {
        OCI_ErrorMemory(con, NULL, mem_size);
        OCI_DirectPathFree(dp);
        return NULL;
    }

    /* allocates direct context handle */
    OCI_CALL(con, OCIHandleAlloc(con->env, (dvoid **) (void *) &dp->ctx, 
                                 OCI_HTYPE_DIRPATH_CTX, 0, (dvoid **) NULL))

    /* set table name attribute */
    OCI_CALL(dp->con, OCIAttrSet(dp->ctx, OCI_HTYPE_DIRPATH_CTX,
                                 (text*) name, (sb4) mtextsize(name),
                                 OCI_ATTR_NAME, dp->con->err))

    /* set array size attribute */
    OCI_CALL(dp->con, OCIAttrSet(dp->ctx, OCI_HTYPE_DIRPATH_CTX, 
                                 (dvoid*) &dp->size, sizeof(ub2),
                                 OCI_ATTR_NUM_ROWS, dp->con->err))  


    /* set columns count attribute */
    OCI_CALL(dp->con, OCIAttrSet(dp->ctx, OCI_HTYPE_DIRPATH_CTX, 
                                 (dvoid*) &dp->table->nb_items, sizeof(ub2),
                                 OCI_ATTR_NUM_COLS, dp->con->err))  

    OCI_CALL(dp->con, OCIAttrGet(dp->ctx, OCI_HTYPE_DIRPATH_CTX, &hlist, 
                                 NULL, OCI_ATTR_LIST_COLUMNS, dp->con->err))

    /* set colunm array attributes */
    cols = dp->table->items;

    for (i = 0; i < dp->table->nb_items; i++)
    {   
        OCI_Column *col = &cols[i];
        OCI_DirectPathItem *item = &dp->items[i];

        /*get colum attribute handle */
        OCI_CALL(dp->con, OCIParamGet(hlist, OCI_DTYPE_PARAM, dp->con->err,
                                      (dvoid**)&hattr,i+1));


        item->sqlcode = col->sqlcode;
        item->bufsize = col->bufsize;

        switch (col->coltype)
        {
            case OCI_CDT_INTEGER:
                
                col->sqlcode = SQLT_INT;
                break;

            case OCI_CDT_DOUBLE:
                
                col->sqlcode = SQLT_FLT;
                break;

            case OCI_CDT_DATETIME:
                
                col->sqlcode = SQLT_DAT;
                col->bufsize = sizeof(OCI_OracleDate);
                item->alloc  = TRUE;
                break;

            case OCI_CDT_TEXT:

                col->sqlcode = SQLT_CHR;
                break;

            case OCI_CDT_TIMESTAMP:
            case OCI_CDT_INTERVAL:
   
                col->sqlcode = SQLT_CHR;

                 col->sqlcode = SQLT_TIMESTAMP_LTZ;
               item->alloc  = TRUE;
                 col->bufsize = sizeof(OCIDateTime *);
   //             col->sqlcode = SQLT_CHR;
 //               col->bufsize = 20;
//                col->sqlcode = SQLT_DAT;
                col->bufsize = 4;//col->sqlsize; //;sizeof(OCI_STRING_TIMESTAMP);

                        
                OCI_CALL(dp->con, OCIAttrSet(hattr, OCI_DTYPE_PARAM,
                                             (dvoid*)OCI_STRING_TIMESTAMP, 
                                             sizeof(OCI_STRING_TIMESTAMP),
                                             OCI_ATTR_DATEFORMAT,dp->con->err))

                break;

            case OCI_CDT_RAW:
            case OCI_CDT_LONG:
            case OCI_CDT_LOB:

                col->sqlcode = SQLT_BIN;
                break;

            default:
        
               OCI_ErrorDatatypes(col->sqlcode);
               OCI_DirectPathFree(dp);
               return NULL;
        }

	    /* column name */
	    OCI_CALL(dp->con, OCIAttrSet(hattr, OCI_DTYPE_PARAM,
                                     (dvoid*) cols[i].name, 
                                     (ub4) mtextsize(col->name),
                                     OCI_ATTR_NAME, dp->con->err))

	    /* column type */
	    OCI_CALL(dp->con, OCIAttrSet(hattr, OCI_DTYPE_PARAM,
                                     (dvoid*) &col->sqlcode, 
                                     0,
                                     OCI_ATTR_DATA_TYPE,dp->con->err))

	    /* column size */
	    OCI_CALL(dp->con, OCIAttrSet(hattr, OCI_DTYPE_PARAM,
                                     (dvoid*) &col->sqlsize, 
                                     0,
                                     OCI_ATTR_DATA_SIZE,dp->con->err))

	    /* column precision */ 
	    OCI_CALL(dp->con, OCIAttrSet(hattr, OCI_DTYPE_PARAM,
                                     (dvoid*) &col->prec, 
                                     0,
                                     OCI_ATTR_PRECISION,dp->con->err))

	    /* column scale */
	    OCI_CALL(dp->con, OCIAttrSet(hattr, OCI_DTYPE_PARAM,
                                     (dvoid*) &col->scale, 
                                     0,
                                     OCI_ATTR_SCALE,dp->con->err))

       	/* free param handle */
        OCI_CALL(dp->con, OCIDescriptorFree (hattr, OCI_DTYPE_PARAM))

        if (item->alloc)
        {
            OCI_ALLOC(item->buffer, mem_size, col->bufsize*size);

            if (item->buffer == NULL)
            {
                OCI_ErrorMemory(con, NULL, mem_size);
                OCI_SchemaFreeTable(table);
                return NULL;
            }
        }
    }
       /* set columns count attribute */
    OCI_CALL(dp->con, OCIAttrSet(dp->ctx, OCI_HTYPE_DIRPATH_CTX, 
                                 (dvoid*) &toto, sizeof(ub1),
                                 OCI_ATTR_DIRPATH_DCACHE_DISABLE, dp->con->err))  
 
    /*prepare direct path operation */
 	OCI_CALL(dp->con, OCIDirPathPrepare(dp->ctx, dp->con->cxt, dp->con->err))

    /* allocates column array handle */
    OCI_CALL(dp->con, OCIHandleAlloc(dp->ctx, (dvoid **) &dp->arr, 
                                     OCI_HTYPE_DIRPATH_COLUMN_ARRAY, 0,
                                     (dvoid **) NULL))

    /* allocates stream handle */
    OCI_CALL(dp->con, OCIHandleAlloc(dp->ctx, (dvoid **) &dp->strm, 
                                     OCI_HTYPE_DIRPATH_STREAM, 0,
                                     (dvoid **) NULL))


    /* cleanup if necessary */
    if (con->status != OCI_SUCCESS)    
    {
        OCI_ErrorHandler(con, NULL);
        OCI_DirectPathFree(dp);
        dp = NULL;
    }

    return dp;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathFree
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathFree(OCI_DirectPath *dp)
{
    ub2 i;

    OCI_CHECK_PTR(OCI_IPC_DIRECTPATH, dp, FALSE);


    for (i = 0; i < dp->table->nb_items; i++)
    {
        OCI_FREE(dp->items[i].buffer);
    }
    
    OCI_FREE(dp->items);


    OCI_HandleFree(dp->arr, OCI_HTYPE_DIRPATH_COLUMN_ARRAY))
    OCI_HandleFree(dp->strm, OCI_HTYPE_DIRPATH_STREAM))
    OCI_HandleFree(dp->ctx, OCI_HTYPE_DIRPATH_CTX))
 
    OCI_SchemaFree(dp->table);

    OCI_FREE(dp);

    return TRUE;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathLoad
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathLoad(OCI_DirectPath *dp)
{
    int i,j, size;
    OCI_Column *cols, *col;
    ub1 *data;
    void *buf;
    OCI_DirectPathItem *item;
    OCI_Date* date_new;
    OCI_OracleDate *date_old;
    OCI_Timestamp *tm;
    OCIDate date;

    OCI_CHECK_PTR(dp, FALSE);

    cols = dp->table->items;

    /* update values */
    for (i = 0; i < dp->table->nb_items; i++)
    { 
        col  = &cols[i];
        item = &dp->items[i];

        size = col->bufsize;

        if (item->alloc)
            data = (ub1*) item->buffer;
        else
            data = (ub1*) item->input;

        for (j = 0; j < dp->size; j++)
        {
            buf =  &data[col->bufsize*j];

            if (col->coltype == OCI_CDT_DATETIME)
            {
                date_old = (OCI_OracleDate *) buf;
                date_new = ((OCI_Date       **) item->input )[j];

                date_old->century = (date_new->value->OCIDateYYYY / 100) + 100;
                date_old->year    = (date_new->value->OCIDateYYYY % 100) + 100;
                date_old->month   =  date_new->value->OCIDateMM;
                date_old->day     =  date_new->value->OCIDateDD;
                date_old->hour    =  date_new->value->OCIDateTime.OCITimeHH + 1;
                date_old->minute  =  date_new->value->OCIDateTime.OCITimeMI + 1;
                date_old->second  =  date_new->value->OCIDateTime.OCITimeSS + 1;
            }
            else if (col->coltype == OCI_CDT_TEXT)
            {
                size = (int) mtextsize(buf);
            }
            else if (col->coltype == OCI_CDT_TIMESTAMP)
            {
  //               int dummy;
    //           date_old = (OCI_OracleDate *) buf;
                tm       = ((OCI_Timestamp **) item->input )[j];

                item->buffer[j] = tm->handle;
                buf =  item->buffer[j];

               
                //OCI_TimestampToText(tm, OCI_STRING_TIMESTAMP, sizeof(OCI_STRING_TIMESTAMP), buf, 3);

               //buf =  &data[4*j];
               //item->buffer[j] = tm->handle;
             //  size = 4; 
                //size = 
               //memcpy(buf, tm->handle, size);
              // buf = &item->buffer[j];
                /*

                
                OCI_TimestampGetDate(tm, &date.OCIDateYYYY,
                                         &date.OCIDateMM,
                                         &date.OCIDateDD);

                OCI_TimestampGetTime(tm, &date.OCIDateTime.OCITimeHH,
                                         &date.OCIDateTime.OCITimeMI,
                                         &date.OCIDateTime.OCITimeSS,
                                         &dummy);

                date_old->century = (date.OCIDateYYYY / 100) + 100;
                date_old->year    = (date.OCIDateYYYY % 100) + 100;
                date_old->month   =  date.OCIDateMM;
                date_old->day     =  date.OCIDateDD;
                date_old->hour    =  date.OCIDateTime.OCITimeHH + 1;
                date_old->minute  =  date.OCIDateTime.OCITimeMI + 1;
                date_old->second  =  date.OCIDateTime.OCITimeSS + 1;

                */
            }

            OCI_DirectPathSetValue(dp, i, j, buf, size);
        }
    }

    /* convert and load */

    return OCI_DirectPathUpdate(dp) && OCI_DirectPathReset(dp);
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathFinish
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathFinish(OCI_DirectPath *dp)
{
    OCI_CHECK_PTR(dp, FALSE);
  
	OCI_CALL(dp->con, OCIDirPathFinish(dp->ctx, dp->con->err))

    OCI_CHECK_ERROR(dp->con, NULL, FALSE);

    return TRUE;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathAbort
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathAbort(OCI_DirectPath *dp)
{
    OCI_CHECK_PTR(dp, FALSE);
  
	OCI_CALL(dp->con, OCIDirPathAbort(dp->ctx, dp->con->err))

    OCI_CHECK_ERROR(dp->con, NULL, FALSE);

    return TRUE;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetValue
 * ------------------------------------------------------------------------ */

boolean OCI_DirectPathSetValue(OCI_DirectPath *dp, int index, int pos, 
                               void *value, int size)
{
    ub1 done;
    
    if (value == NULL)
        done = OCI_DIRPATH_COL_NULL;
    else
        done = OCI_DIRPATH_COL_COMPLETE;

    OCI_CHECK_PTR(dp, FALSE);

    OCI_CALL(dp->con, OCIDirPathColArrayEntrySet(dp->arr, 
                                                 dp->con->err,
                                                 pos, index,
                                                 (ub1*) value,
                                                 size, done))
    OCI_CHECK_ERROR(dp->con, NULL, FALSE);

    return TRUE;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetArray
 * ------------------------------------------------------------------------ */

boolean OCI_DirectPathSetArray(OCI_DirectPath *dp, int index, void *value)
{
    OCI_CHECK_PTR(dp, FALSE);
    OCI_CHECK_PTR(value, FALSE);
    OCI_CHECK_PTR(dp->table, FALSE);
    OCI_CHECK(index <= 0, FALSE);
    OCI_CHECK(index > (int) dp->table->nb_items, FALSE);

    dp->items[index-1].input = value;

    return TRUE;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetNullAtPos
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathSetNullAtPos(OCI_DirectPath *dp, int index,
                                           int position)
{
    return OCI_DirectPathSetValue(dp, index, position, NULL, 0);
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetArrayOfInts
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathSetArrayOfInts(OCI_DirectPath *dp, int index, 
                                             int value[])
{
    return OCI_DirectPathSetArray(dp, index, value);
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetArrayOfStrings
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathSetArrayOfStrings(OCI_DirectPath *dp, int index, 
                                                dtext value[])
{
    return OCI_DirectPathSetArray(dp, index, value);
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetArrayOfDoubles
 * ------------------------------------------------------------------------ */

OCI_EXPORT boolean OCI_API OCI_DirectPathSetArrayOfDoubles(OCI_DirectPath *dp, 
                                                           int index,
                                                           double value[])
{
    return OCI_DirectPathSetArray(dp, index, value);
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetArrayOfDates
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathSetArrayOfDates(OCI_DirectPath *dp, int index,
                                              OCI_Date *value[])
{
    OCI_Column *cols = (OCI_Column *) dp->table->items;
    
    OCI_CHECK_PTR(cols, FALSE);
    OCI_CHECK_BOOL(cols[index-1].coltype == OCI_CDT_DATETIME, FALSE)

    OCI_DirectPathSetArray(dp, index, value);

    /* allocate arraty*/
    return TRUE;
}

/* ------------------------------------------------------------------------ *
 * OCI_DirectPathSetArrayOfTimestamps
 * ------------------------------------------------------------------------ */

boolean OCI_API OCI_DirectPathSetArrayOfTimestamps(OCI_DirectPath *dp, int index,
                                                   OCI_Timestamp *value[])
{
    OCI_Column *cols = (OCI_Column *) dp->table->items;
    
    OCI_CHECK_PTR(cols, FALSE);
    OCI_CHECK_BOOL(cols[index-1].coltype == OCI_CDT_TIMESTAMP, FALSE)

    OCI_DirectPathSetArray(dp, index, value);

    /* allocate arraty*/
    return TRUE;
}


